package luzhin;

import java.util.*;
import java.io.*;

/**
 * Move generator. DOES NOT test that the move is not
 * to check (though tests castling). 
 * @author lacungus
 *
 */
public class SimpleMoveGenerator implements Serializable {
	private final static long serialVersionUID = 2834902348L;
	private boolean whiteToMove;
	private Board board;
	private LinkedList<Move> result;
	private AttackDiagramBuilder attackDiagramBuilder = null;
	private AttackDiagram diagram = null;

	public SimpleMoveGenerator() {
		attackDiagramBuilder = new AdvancedAttackDiagramBuilder();
	}
	
	private void addKnightMoves(int start) {
		int startI = start / Board.LEN;
		int startJ = start % Board.LEN;
		
		int[] dx = {1, 1, -1, -1, 2, 2, -2, -2};
		int[] dy = {2, -2, 2, -2, 1, -1, 1, -1};
		
		int startFigure = board.getPiece(start);
		
		for (int k=0;k<dx.length;++k) {
			int i = startI + dx[k];
			int j = startJ + dy[k];
			
			int curPos = Position.getPosition(i, j);
			if (curPos == -1)
				continue;
			if (Piece.getColor(board.getPiece(curPos)) == Piece.getColor(startFigure))
				continue;
			result.add(new Move(start, curPos));
		}		
	}

	private void addPawnMoves(int start) {
		int i = start / Board.LEN;
		int j = start % Board.LEN;
		int startFigure = board.getPiece(start);
		int color = Piece.getColor(startFigure);
		
		if (color == 1) {
			boolean onStart = (i == 1);
			int nextPos = Position.getPosition(i + 1, j);
			if (nextPos != -1 && Piece.getColor(board.getPiece(nextPos)) == 0) {
				result.add(new Move(start, nextPos));
				nextPos =  Position.getPosition(i + 2, j);
				if (onStart && nextPos != -1 && Piece.getColor(board.getPiece(nextPos)) == 0) {
					result.add(new Move(start, nextPos));
				}
			}
			
			nextPos = Position.getPosition(i + 1, j + 1);
			if (nextPos != -1 && Piece.getColor(board.getPiece(nextPos)) == -1) {
				result.add(new Move(start, nextPos));
			}
			
			nextPos = Position.getPosition(i + 1, j - 1);
			if (nextPos != -1 && Piece.getColor(board.getPiece(nextPos)) == -1) {
				result.add(new Move(start, nextPos));
			}			
		} else {
			boolean onStart = (i == 6);
			int nextPos = Position.getPosition(i - 1, j);
			if (nextPos != -1 && Piece.getColor(board.getPiece(nextPos)) == 0) {
				result.add(new Move(start, nextPos));
				nextPos =  Position.getPosition(i - 2, j);
				if (onStart && nextPos != -1 && Piece.getColor(board.getPiece(nextPos)) == 0) {
					result.add(new Move(start, nextPos));
				}
			}
			
			nextPos = Position.getPosition(i - 1, j + 1);
			if (nextPos != -1 && Piece.getColor(board.getPiece(nextPos)) == 1) {
				result.add(new Move(start, nextPos));
			}
			
			nextPos = Position.getPosition(i - 1, j - 1);
			if (nextPos != -1 && Piece.getColor(board.getPiece(nextPos)) == 1) {
				result.add(new Move(start, nextPos));
			}						
		}
	}
	
	private void addBishopMoves(int start) {
		int startI = start / Board.LEN;
		int startJ = start % Board.LEN;
		
		int[] dx = {1, 1, -1, -1};
		int[] dy = {1, -1, 1, -1};
		
		int startFigure = board.getPiece(start);
		
		for (int k=0;k<dx.length;++k) {
			int i = startI;
			int j = startJ;
			
			while (true) {
				i += dx[k];
				j += dy[k];
				int curPos = Position.getPosition(i, j);
				if (curPos == -1)
					break;
				if (Piece.getColor(board.getPiece(curPos)) == Piece.getColor(startFigure))
					break;
				result.add(new Move(start, curPos));
				if (Piece.getColor(board.getPiece(curPos)) == -Piece.getColor(startFigure))
					break;
			}
		}		
	}
	
	private void addRookMoves(int start) {
		int startI = start / Board.LEN;
		int startJ = start % Board.LEN;
		
		int[] dx = {0, 0, 1, -1};
		int[] dy = {-1, 1, 0, 0};
		
		int startFigure = board.getPiece(start);
		
		for (int k=0;k<dx.length;++k) {
			int i = startI;
			int j = startJ;
			
			while (true) {
				i += dx[k];
				j += dy[k];
				int curPos = Position.getPosition(i, j);
				if (curPos == -1)
					break;
				if (Piece.getColor(board.getPiece(curPos)) == Piece.getColor(startFigure))
					break;
				result.add(new Move(start, curPos));
				if (Piece.getColor(board.getPiece(curPos)) == -Piece.getColor(startFigure))
					break;
			}
		}
	}
	
	private boolean canCastleShort(int start) {
		boolean res = true;
		
		if (whiteToMove && (board.isWhiteKingMoved() || board.isRookH1Moved()))
				return false;
		if (!whiteToMove && (board.isBlackKingMoved() || board.isRookH8Moved()))
			return false;
		
		for (int k=1;k<=2;++k)			
			if (board.getPiece(start + k) != Piece.NONE)
				res = false;		

		//tests squares passed by the king for checks
		for (int k=start;k<=start+2;++k) {			
			if (board.isWhiteToMove() && diagram.getBlackAttacks()[k] > 0 || !board.isWhiteToMove() && diagram.getWhiteAttacks()[k] > 0)
				res = false;	
		}
		return res;
	}
	
	private boolean canCastleLong(int start) {
		boolean res = true;
		
		if (whiteToMove && (board.isWhiteKingMoved() || board.isRookA1Moved()))
			return false;
		if (!whiteToMove && (board.isBlackKingMoved() || board.isRookA8Moved()))
			return false;
	
		for (int k=1;k<=3;++k) {			
			if (board.getPiece(start - k) != Piece.NONE)
				res = false;	
		}
		//tests squares passed by the king for checks
		for (int k=start;k>=start-2;--k) {			
			if (board.isWhiteToMove() && diagram.getBlackAttacks()[k] > 0 || !board.isWhiteToMove() && diagram.getWhiteAttacks()[k] > 0)
				res = false;	
		}
		return res;
	}
	
	private void addKingMoves(int start) {
		int startI = start / Board.LEN;
		int startJ = start % Board.LEN;
		
		int[] dx = {0, 0, 1, -1, 1, 1, -1, -1};
		int[] dy = {-1, 1, 0, 0, 1, -1, 1, -1};
		
		int startFigure = board.getPiece(start);
		
		for (int k=0;k<dx.length;++k) {
			int i = startI + dx[k];
			int j = startJ + dy[k];
			int curPos = Position.getPosition(i, j);
			if (curPos == -1)
				continue;
			if (Piece.getColor(board.getPiece(curPos)) == Piece.getColor(startFigure))
				continue;
			result.add(new Move(start, curPos));
		}
		
		if (canCastleShort(start)) {
			result.add(new Move(start, start + 2));
		}
		if (canCastleLong(start)) {
			result.add(new Move(start, start - 2));
		}
	}
	
	private void addQueenMoves(int start) {
		int startI = start / Board.LEN;
		int startJ = start % Board.LEN;
		
		int[] dx = {0, 0, 1, -1, 1, 1, -1, -1};
		int[] dy = {-1, 1, 0, 0, 1, -1, 1, -1};
		
		int startFigure = board.getPiece(start);
		
		for (int k=0;k<dx.length;++k) {
			int i = startI;
			int j = startJ;
			
			while (true) {
				i += dx[k];
				j += dy[k];
				int curPos = Position.getPosition(i, j);
				if (curPos == -1)
					break;
				if (Piece.getColor(board.getPiece(curPos)) == Piece.getColor(startFigure))
					break;
				result.add(new Move(start, curPos));
				if (Piece.getColor(board.getPiece(curPos)) == -Piece.getColor(startFigure))
					break;
			}
		}
	}

	public LinkedList<Move> generateMoves(Board board) {
		diagram = attackDiagramBuilder.buildAttackDiagram(board);
		return generateMoves(board, diagram);
	}

/**
 * 
 * @param board
 * @return Possible moves.
 */
	public LinkedList<Move> generateMoves(Board board, AttackDiagram diagram) {
		this.board = board;
		this.diagram = diagram;
		
		result = new LinkedList<Move>();
			
		whiteToMove = board.isWhiteToMove();
		for (int i=0;i<Board.SIZE;++i) {
			int curFigure = board.getPiece(i);
			int color = Piece.getColor(curFigure);
			if (color == 0)
				continue;
			if ((color == 1) == whiteToMove) {
				if (curFigure== Piece.BR || curFigure == Piece.WR) {
					addRookMoves(i);
					continue;
				}
				if (curFigure == Piece.BN || curFigure == Piece.WN) {
					addKnightMoves(i);
					continue;
				}
				if (curFigure == Piece.BB || curFigure == Piece.WB) {
					addBishopMoves(i);
					continue;
				}
				if (curFigure == Piece.BQ || curFigure == Piece.WQ) {
					addQueenMoves(i);
					continue;
				}
				if (curFigure == Piece.BK || curFigure == Piece.WK) {
					addKingMoves(i);
					continue;
				}				
				if (curFigure == Piece.BP || curFigure == Piece.WP) {
					addPawnMoves(i);
					continue;
				}				
			}
		}
		
		return result;
	}
}
